#pragma once

#include <algorithm>
#include <cstddef>
#include <fstream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

#include "arithmetic.h"
#include "ir.h"
#include "irblock.h"
#include "irfunction.h"
#include "iroperand.h"
#include "pass.h"
#include "utils.h"

class IRTest : public Pass {
    using LibFunc = std::function<ArithValue(const std::vector<ArithValue>&)>;
    using ArgList = std::vector<ArithValue>;

    std::istringstream ifs;
    std::ostream& ofs;
    IRModule* module;
    std::string entry;
    std::vector<ArithValue> entryArgs;
    int memPtr = 0;

    ArithValue& GetValue(IR::Operand* x);

    IRBlock* Step(IR::IRBase* irBase);

    void Init();

    ArithValue Call(IRFunction* func, const std::vector<ArithValue>& args);

    ArithValue CallLib(const std::string& name, const ArgList& args);

  public:
    std::unordered_map<int, ArithValue> mem;
    std::unordered_map<std::string, ArithValue> global;
    std::unordered_map<std::string, ArithValue> var;

    IRTest(std::string in = "", std::ostream& out = std::cout,
           std::string entry = "main", const std::vector<ArithValue>& args = {})
        : ifs(std::move(in)),
          ofs(out),
          entry(std::move(entry)),
          entryArgs(args) {}

    virtual void* Run(void* input) override;
};
